package com.lucy.management.utils;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public class GenerateEntityMysql {

    private static final Logger log = LoggerFactory.getLogger(GenerateEntityMysql.class);

    /*
     * 注意：需要同时生成 Mapper 时，请先 refresh F5 实体包，然后再 refresh F5 Mapper 包，不然可能出现报错（找不到实体文件）！！！
     *
     * */

    private String packagePath = "com.yqsh.payment.domain.base";    // 指定实体生成所在包的路径，例如：com.demo.entity
    private String mapperPackagePath = "com.demo.mapper.test"; // 指定实体对应 Mapper 接口所在包的路径，例如：com.demo.mapper
    private boolean mapperFlag = false;        // 是否同时生成 Mapper 接口文件，默认为是
    private String authorName = "lucy";    // 作者名
    private String[] tableNames = null;    // null 时，生成所有表的实体类；也可以指定生成某些表的实体类，例如：{"t_user", "t_user", ...}

    // 数据库连接
    private static final String URL = "jdbc:mysql://192.168.2.7:3306/yqsh_payment_platform";
    private static final String NAME = "root";
    private static final String PASS = "123456";
    private static final String DRIVER = "com.mysql.jdbc.Driver";

    private Connection conn;
    private PreparedStatement ps;
    private ResultSetMetaData rsmd;
    private DatabaseMetaData dmd;
    private ResultSet rs;

    public static void main(String[] args) {
        log.info(String.format("[%s] 开始生成实体类", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
        long start = System.currentTimeMillis();
        new GenerateEntityMysql();
        long end = System.currentTimeMillis();
        log.info(String.format("[%s] 结束生成实体类，耗时：%s毫秒", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), (end - start)));

    }

    public GenerateEntityMysql() {

        String[] colNames; // 列名数组
        String[] colTypes; // 列名类型数组
        int[] colSizes; // 列名大小数组
        boolean i_util = false; // 是否需要导入包java.util.*
        boolean i_sql = false; // 是否需要导入包java.sql.*
        boolean i_auto_increment = false; // 自增长
        String primary = "id"; // 主键字段

        try {
            Class.forName(DRIVER);
            conn = DriverManager.getConnection(URL, NAME, PASS);
            if (tableNames == null || tableNames.length == 0) {
                dmd = conn.getMetaData();
                rs = dmd.getTables(null, "%", "%", new String[]{"TABLE"});
                List<String> list = new ArrayList<String>();
                while (rs.next()) {
                    list.add(rs.getString("TABLE_NAME"));
                }
                tableNames = list.toArray(new String[]{});
            }
            if (tableNames == null || tableNames.length == 0) {
                log.info("没有找到任何相关的表");
            } else {
                for (String tableName : tableNames) {
                    String sql = "select * from " + tableName;
                    ps = conn.prepareStatement(sql);
                    rsmd = ps.getMetaData();
                    int size = rsmd.getColumnCount(); // 统计列
                    colNames = new String[size];
                    colTypes = new String[size];
                    colSizes = new int[size];
                    for (int i = 0; i < size; i++) {
                        colNames[i] = rsmd.getColumnName(i + 1);
                        colTypes[i] = rsmd.getColumnTypeName(i + 1);

                        if (rsmd.isAutoIncrement(i + 1)) {
                            i_auto_increment = true;
                            primary = rsmd.getColumnName(i + 1);
                        }
                        if (colTypes[i].equalsIgnoreCase("datetime")) {
                            i_util = true;
                        }
                        if (colTypes[i].equalsIgnoreCase("image") || colTypes[i].equalsIgnoreCase("text")) {
                            i_sql = true;
                        }
                        colSizes[i] = rsmd.getColumnDisplaySize(i + 1);
                    }

                    String content = parse(tableName, colNames, colTypes, colSizes, i_util, i_sql, i_auto_increment, primary);
                    generateEntity(tableName, content);

                    if (mapperFlag) {
                        String mapperContent = parseMapper(tableName);
                        generateMapper(tableName, mapperContent);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (ps != null) {
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 生成实体
     *
     * @author
     */
    private void generateEntity(String tableName, String content) {
        try {
            File directory = new File("");
            // log.info("绝对路径："+directory.getAbsolutePath());
            // log.info("相对路径："+directory.getCanonicalPath());
            // String path = this.getClass().getResource("").getPath();

            File dir = new File(directory.getAbsolutePath() + "/src/main/java/" + this.packagePath.replace(".", "/"));
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File file = new File(directory.getAbsolutePath() + "/src/main/java/" + this.packagePath.replace(".", "/") + "/"
                    + upperCamelCase(tableName) + ".java");
            if (!dir.exists()) {
                dir.createNewFile();
            }
            log.info(tableName + " 表对应实体文件路径：" + file);
            PrintWriter pw = new PrintWriter(file);
            pw.println(content);
            pw.flush();
            pw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成 Mapper 接口文件
     *
     * @author
     */
    private void generateMapper(String tableName, String content) {
        try {
            File directory = new File("");
            File dir = new File(directory.getAbsolutePath() + "/src/main/java/" + this.mapperPackagePath.replace(".", "/"));
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File file = new File(directory.getAbsolutePath() + "/src/main/java/" + this.mapperPackagePath.replace(".", "/") + "/I" + upperCamelCase(tableName) + "Mapper.java");
            if (!dir.exists()) {
                dir.createNewFile();
            }
            log.info(tableName + " 表对应Mapper文件路径：" + file);
            PrintWriter pw = new PrintWriter(file);
            pw.println(content);
            pw.flush();
            pw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 分析数据，生产文件所需内容
     *
     * @author
     */
    private String parse(String tableName, String[] colNames, String[] colTypes, int[] colSizes, boolean i_util,
                         boolean i_sql, boolean i_auto_increment, String primary) {
        StringBuffer sb = new StringBuffer();

        sb.append("package ").append(this.packagePath).append(";\n\n");

        sb.append("import java.io.Serializable;\n\n")
                .append("import lombok.Data;\n\n")
                .append("import lombok.ToString;\n\n")
                .append("import org.hibernate.annotations.GenericGenerator;\n\n")
                .append("import javax.persistence.*;\n\n");

        // 判断是否导入工具包
        if (i_util) {
            sb.append("import java.util.Date;\n");
        }
        if (i_sql) {
            sb.append("import java.sql.*;\n");
        }

        if (i_auto_increment) {
            sb.append("import javax.persistence.GeneratedValue;\n");
            sb.append("import javax.persistence.GenerationType;\n");
            sb.append("import javax.persistence.Id;\n\n");
        }

        // 注释部分
        sb.append("/**\n  * \n  * ")
                .append(tableName)
                .append(" 实体类\n")
                .append("  *\n  * @date ")
                .append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS ").format(new Date()))
                .append("\n  * @author ")
                .append(this.authorName)
                .append("\n")
                .append("  */ \n");
        //注解部分

        sb.append("@Data\n\n")
                .append("@ToString\n\n")
                .append("@Entity\n\n")
                .append("@Table(name=\"").append(tableName).append("\")\n\n")
                .append("@GenericGenerator(name = \"jpa-uuid\", strategy = \"uuid\")\n\n");

        // 实体部分
        sb.append("public class ")
                .append(upperCamelCase(tableName))
                .append(" implements Serializable {\n\n")
                .append("\tprivate static final long serialVersionUID = 1L;\n\n");

        processAllAttrs(colNames, colTypes, primary, sb); // 属性
        sb.append("\n");
        // processAllMethod(colNames, colTypes, sb); // get/set方法
        sb.append("}");

        return sb.toString();
    }

    /**
     * 生产文件所需内容
     *
     * @author
     */
    private String parseMapper(String tableName) {
        StringBuffer sb = new StringBuffer();

        sb.append("package ").append(this.mapperPackagePath).append(";\n\n");

        sb.append("import ").append(this.packagePath).append(".").append(upperCamelCase(tableName)).append(";\n\n");

        // 导入相关包
        sb.append("import tk.mybatis.mapper.common.Mapper;\n");
        sb.append("import tk.mybatis.mapper.common.MySqlMapper;\n\n");

        // 注释部分
        sb.append("/**\n  * \n  * ")
                .append("I")
                .append(upperCamelCase(tableName))
                .append("Mapper 接口\n")
                .append("  *\n  * @date ")
                .append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS ").format(new Date()))
                .append("\n  * @author ")
                .append(this.authorName)
                .append("\n")
                .append("  */ \n");
        // 实体部分
        sb.append("public interface I")
                .append(upperCamelCase(tableName))
                .append("Mapper extends Mapper<")
                .append(upperCamelCase(tableName))
                .append(">, MySqlMapper<")
                .append(upperCamelCase(tableName))
                .append("> {\n\n");

        sb.append("}");

        return sb.toString();
    }

    /**
     * 生产所有属性
     *
     * @param colNames 列名数组
     * @param colTypes 列名类型数组
     * @param primary  主键字段
     * @param sb       存放内容
     * @return 所有属性内容
     * @author
     */
    private void processAllAttrs(String[] colNames, String[] colTypes, String primary, StringBuffer sb) {
        for (int i = 0; i < colNames.length; i++) {
            if (primary.equals(colNames[i])) {
                sb.append("\t@Id\r\n");
                sb.append("\t@GeneratedValue(generator = \"jpa-uuid\")\r\n")
                .append("\t@Column(length = 32)\r\n");
            }
            // sb.append("\tprivate " + sqlType2JavaType(colTypes[i]) + " " + lowerCamelCase(colNames[i]) + ";\r\n");
            sb.append("\tprivate " + sqlType2JavaType(colTypes[i]) + " " + colNames[i] + ";\r\n");
        }
    }

    /**
     * 生产所有set/get方法
     *
     * @param colNames 列名数组
     * @param colTypes 列名类型数组
     * @param sb       存放内容
     * @return 所有set/get方法内容
     * @author
     */
    private void processAllMethod(String[] colNames, String[] colTypes, StringBuffer sb) {
        for (int i = 0; i < colNames.length; i++) {
            sb.append("\tpublic void set" + upperCamelCase(colNames[i]) + "(" + sqlType2JavaType(colTypes[i]) + " "
                    + lowerCamelCase(colNames[i]) + "){\r\n");
            sb.append("\t\tthis." + lowerCamelCase(colNames[i]) + " = " + lowerCamelCase(colNames[i]) + ";\r\n");
            sb.append("\t}\r\n");
            sb.append("\tpublic " + sqlType2JavaType(colTypes[i]) + " get" + upperCamelCase(colNames[i]) + "(){\r\n");
            sb.append("\t\treturn " + lowerCamelCase(colNames[i]) + ";\r\n");
            sb.append("\t}\r\n");
        }

    }

    /**
     * 大驼峰命名法
     *
     * @author
     */
    public String upperCamelCase(String str) {
        str = str.toLowerCase();
        String result = "";
        String[] names = str.split("_");
        for (int i = 0; i < names.length; i++) {
            result += names[i].substring(0, 1).toUpperCase() + names[i].substring(1);
        }
        return result;
    }

    /**
     * 小驼峰命名法
     *
     * @author
     */
    public String lowerCamelCase(String str) {
        str = str.toLowerCase();
        String result = "";
        String[] names = str.split("_");
        for (int i = 0; i < names.length; i++) {
            if (i == 0)
                result += names[i].substring(0, 1).toLowerCase() + names[i].substring(1);
            else
                result += names[i].substring(0, 1).toUpperCase() + names[i].substring(1);
        }
        return result;
    }

    /**
     * 将SQL数据类型转换为Java数据类型
     *
     * @param sqlType SQL字段类型
     * @return Java数据类型
     * @author
     */
    private String sqlType2JavaType(String sqlType) {

        if (sqlType.equalsIgnoreCase("bit")) {
            return "Boolean";
        } else if (sqlType.equalsIgnoreCase("tinyint")) {
            return "Integer";
        } else if (sqlType.equalsIgnoreCase("smallint")) {
            return "Short";
        } else if (sqlType.equalsIgnoreCase("int")) {
            return "Integer";
        } else if (sqlType.equalsIgnoreCase("bigint")) {
            return "Long";
        } else if (sqlType.equalsIgnoreCase("float")) {
            return "Float";
        } else if (sqlType.equalsIgnoreCase("decimal") || sqlType.equalsIgnoreCase("numeric")
                || sqlType.equalsIgnoreCase("real") || sqlType.equalsIgnoreCase("money")
                || sqlType.equalsIgnoreCase("smallmoney")) {
            return "Double";
        } else if (sqlType.equalsIgnoreCase("varchar") || sqlType.equalsIgnoreCase("char")
                || sqlType.equalsIgnoreCase("nvarchar") || sqlType.equalsIgnoreCase("nchar")
                || sqlType.equalsIgnoreCase("text")) {
            return "String";
        } else if (sqlType.equalsIgnoreCase("datetime") || sqlType.equalsIgnoreCase("date")) {
            return "Date";
        } else if (sqlType.equalsIgnoreCase("image")) {
            return "Blod";
        }

        return null;
    }


}

